import configparser
import os
from datetime import time
from typing import Optional, Tuple

from appium import webdriver
from appium.options.android import UiAutomator2Options
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.remote.webelement import WebElement

from config.config import SCREENSHOTS_PATH

appium_server_url = 'http://127.0.0.1:4723'


def open_app(capabilities):
    """获取driver的方法"""
    return webdriver.Remote(appium_server_url, options=UiAutomator2Options().load_capabilities(capabilities))


def open_app2(app_name="xiachufang-app"):
    """
    根据指定的应用配置名称，创建并返回一个已连接到该应用的 Appium WebDriver 实例。

    参数
    ----------
    app_name : str, 可选
        配置文件中定义的应用名称，默认为 "xiachufang-app"。
        该名称用于从配置处理器（ConfigHandler）中读取对应的 Desired Capabilities。

    返回
    ----------
    driver : appium.webdriver.Remote
        已初始化并连接到目标应用的 WebDriver 实例，已设置 10 秒隐式等待。
	"""
    ch = ConfigHandler()
    dtc = ch.read_capabilities(app_name)
    driver = webdriver.Remote(appium_server_url, options=UiAutomator2Options().load_capabilities(dtc))
    driver.implicitly_wait(10)
    return driver


class ConfigHandler:
    def __init__(self):
        # 获取 config.ini 绝对路径，适配项目结构
        config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config", "config.ini")
        self.config = configparser.ConfigParser()
        self.config.optionxform = str  # 维持原始键名大小写
        self.config.read(config_path)

    def _str_to_bool(self, z):
        """纯bool处理函数名前面加_只能在类的内部使用"""
        return z.strip().lower() in ('1', 'yes', 'true', 'on', 'false')

    def read(self, section, key):
        """读取指定扇区的指定键的值"""
        return self.config.get(section, key)

    def read_section(self, section):
        """读取指定扇区的所有键值对"""
        options = self.config.items(section)
        dct = {}
        for key, value in options:
            if self._str_to_bool(value):
                dct[key] = True
            else:
                dct[key] = value
        return dct

    def read_capabilities(self, app_name):
        # 先读需要的页面的app信息
        app_dict = self.read_section(app_name)
        # 再读取基本的app信息
        base_dict = self.read_section("base-capabilities")
        # 合并并返回
        app_dict.update(base_dict)
        return app_dict


class Swipe:
    """封装滑动方法，支持上下左右滑动"""

    def __init__(self, driver):
        self.driver = driver
        size = self.driver.get_window_size()
        self.width, self.height = size['width'], size['height']

    def _swipe(self, start_x, start_y, end_x, end_y, duration=500):
        """内部通用滑动方法"""
        self.driver.swipe(
            start_x=start_x,
            start_y=start_y,
            end_x=end_x,
            end_y=end_y,
            duration=duration
        )

    def up_swipe(self, duration=500):
        """上滑：从屏幕下方 80% 处滑到上方 20% 处"""
        start_x = self.width * 0.5
        start_y = self.height * 0.8
        end_x = self.width * 0.5
        end_y = self.height * 0.2
        self._swipe(start_x, start_y, end_x, end_y, duration)

    def down_swipe(self, duration=500):
        """下滑：从屏幕上方 20% 处滑到下方 80% 处"""
        start_x = self.width * 0.5
        start_y = self.height * 0.2
        end_x = self.width * 0.5
        end_y = self.height * 0.8
        self._swipe(start_x, start_y, end_x, end_y, duration)

    def left_swipe(self, duration=500):
        """左滑：从屏幕右侧 80% 处滑到左侧 20% 处"""
        start_x = self.width * 0.8
        start_y = self.height * 0.5
        end_x = self.width * 0.2
        end_y = self.height * 0.5
        self._swipe(start_x, start_y, end_x, end_y, duration)

    def right_swipe(self, duration=500):
        """右滑：从屏幕左侧 20% 处滑到右侧 80% 处"""
        start_x = self.width * 0.2
        start_y = self.height * 0.5
        end_x = self.width * 0.8
        end_y = self.height * 0.5
        self._swipe(start_x, start_y, end_x, end_y, duration)


def drag_and_drop_w3c(
        driver,
        source: WebElement,
        target_element: Optional[WebElement] = None,
        target_point: Optional[Tuple[int, int]] = None,
        hold_time: float = 0.2,
        drag_time: float = 0.5,
        lift_button: int = 0,
):
    """
    使用 W3C Actions（ActionBuilder + PointerInput）。

    参数：
      - driver: Appium webdriver（已建立 session）
      - source: 起始元素（WebElement）
      - target_element: 目标元素（二选一）
      - target_point: 目标坐标 (x, y)（二选一；相对于 viewport）
      - hold_time: 按住源元素多长时间（秒，float）。如果为0则不额外 pause。
      - drag_time: 从起始点移动到目标所需时间（秒，float）
      - lift_button: pointerUp 时的 button（touch 情况下通常传 0）

    注意：
      * 必须提供 target_element 或 target_point 其中之一。
      * 内部把 drag_time 转为毫秒传给 create_pointer_move；create_pause 接受秒值。
      * 使用元素为 origin 时，传 (duration=..., x=0, y=0, origin=element) 会把 pointer 移到元素的中心点。
    """

    if not (target_element or target_point):
        raise ValueError("必须提供 target_element 或 target_point 其一。")

    # 构造 ActionBuilder 并添加一个 touch 类型的 PointerInput
    action = ActionBuilder(driver)
    finger = action.add_pointer_input(kind="touch", name="finger1")  # 返回 PointerInput

    # 1) 先把指针移动到 source 元素的中心（duration=0 立刻移动）
    finger.create_pointer_move(duration=0, x=0, y=0, origin=source)

    # 2) 按下（touch down）
    finger.create_pointer_down()

    # 3) 可选的按住（秒）：create_pause 接受秒并在内部转换为 ms
    if hold_time and hold_time > 0:
        finger.create_pause(hold_time)

    # 4) 拖动到目标（支持元素或坐标）
    #    create_pointer_move 的 duration 单位是毫秒，所以这里把 seconds -> ms
    move_duration_ms = int(max(0, int(drag_time)) * 1000)
    if target_element:
        # 以目标元素中心为目标（x=0,y=0 表示元素中心）
        finger.create_pointer_move(duration=move_duration_ms, x=0, y=0, origin=target_element)
    else:
        tx, ty = target_point
        finger.create_pointer_move(duration=move_duration_ms, x=int(tx), y=int(ty), origin=None)

    # 5) 松开（pointer up）
    finger.create_pointer_up(lift_button)

    # 6) 执行动作
    action.perform()

    # 可选：清理已构建动作（下一次构建新动作时更干净）
    try:
        action.clear_actions()
    except Exception:
        # 某些 client 版本可能没有实现 clear_actions；忽略即可
        pass


def save_screenshot(driver, case_name):
    """
    保存截图
    :param driver: appium driver
    :param case_name: 测试用例名称
    :return: 截图文件路径
    """
    screenshot_dir = SCREENSHOTS_PATH
    if not os.path.exists(screenshot_dir):
        os.makedirs(screenshot_dir)

    file_name = f"{case_name}_{time.strftime('%Y%m%d_%H%M%S')}.png"
    file_path = os.path.join(screenshot_dir, file_name)

    driver.save_screenshot(file_path)
    return file_path
